package com.gitee.wsl.mathematics.matrix.mat4.ext

import com.gitee.wsl.mathematics.matrix.mat4.MutableMatrix4
import com.gitee.wsl.unit.angle.AngleUnit
import kotlin.math.PI
import kotlin.math.tan


fun<T:Number> MutableMatrix4<T, *, *>.setToPerspective(
    fovy: T, aspect: T,
    near: T, far: T) {

    val f = 1f / tan(fovy * (PI / 360.0))
    val rangeReciprocal = 1f / (near - far)

    m[0].set(f / aspect,zero,zero,zero)
    m[1].set(zero,          f,  zero,zero)
    m[2].set(zero,          zero,(far + near) * rangeReciprocal,2f * far * near * rangeReciprocal)
    m[3].set(zero,          zero,-one,zero)
    
    /*
    data[0] = f / aspect
    data[1] = 0f
    data[2] = 0f
    data[3] = 0f

    data[4] = 0f
    data[5] = f
    data[6] = 0f
    data[7] = 0f

    data[8] = 0f
    data[9] = 0f
    data[10] = (far + near) * rangeReciprocal
    data[11] = -1f

    data[12] = 0f
    data[13] = 0f
    data[14] = 2f * far * near * rangeReciprocal
    data[15] = 0f
    */
}

/**
 * Inplace operation: Applies a perspective projection transform to this matrix.
 */
fun<T:Number> MutableMatrix4<T, *, *>.perspective(
    fovy: AngleUnit, aspect: T,
    near: T, far: T,
    depthRange: DepthRange) {

    val f = 1.0f / tan(fovy.radian / 2.0f) + zero
    val rangeRecip = 1.0f / (near - far)

    val z: T
    val zt: T
    when (depthRange) {
        DepthRange.NEGATIVE_ONE_TO_ONE -> {
            z = (far + near) * rangeRecip
            zt = 2.0f * far * near * rangeRecip
        }
        DepthRange.ZERO_TO_ONE -> {
            z = far * rangeRecip
            zt = far * near * rangeRecip
        }
    }

    return mul(
        f / aspect, zero, zero, zero,
        zero, f, zero, zero,
        zero, zero, z, zt,
        zero, zero, -one, zero
    )
}

/**
 * Inplace operation: Applies a perspective projection transform for reversed depth with an infinite far plane
 * to this matrix.
 */
fun<T:Number> MutableMatrix4<T, *, *>.perspectiveReversedDepthInfiniteRange(fovy: AngleUnit, aspect: T, near: T) {
    val f = 1.0 / tan(fovy.radian / 2.0) + zero
    return mul(
        f / aspect, zero, zero, zero,
        zero, f, zero, zero,
        zero, zero, zero, near,
        zero, zero, -one, zero
    )
}